package com.jasperframework.boot.common.utils;

import com.jasperframework.boot.common.enums.DownloadFileFormatEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.*;
import java.util.List;

/**
  * <p> 下载文件工具类  前端超链接 get的下载方式 </p>
  *
  * @site https://miliqkdoc.motopa.cn/
  * @author daizhao
  * @date 2022/12/12 12:12
  */
@Slf4j
public class DownloadFileUtils {


    /**
     * 下载文件
     * @param file              待下载文件
     * @param outPutFileName    下载文件名称
     * @param formate           下载文件格式
     * @param response          response
     * @throws IOException
     */
    public static void downloadFile(File file, String outPutFileName, DownloadFileFormatEnum formate, HttpServletResponse response) throws IOException {
        // 设置response参数，可以打开下载页面
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Cache-Control", "private");
        response.setHeader("Pragma", "private");
        response.setHeader("Content-Type", "application/force-download");

        //String fileName = new String (URLEncoder.encode(filename, "UTF-8"));
        //String fileName = new String (URLEncoder.encode(filename, "UTF-8"));//防止下载文件乱码
        //防止下载文件乱码
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outPutFileName, "UTF-8").concat(formate.getSuffix()));

        switch (formate){
            case RAR:
                response.setContentType("application/octet-stream;charset=utf-8");
                processZip(response.getOutputStream(), file, outPutFileName, formate, true);
                break;
            case TXT:
                response.setContentType("text/plain;charset=utf-8");
                processCommon(response.getOutputStream(), file, true);
                break;
            case ZIP:
                response.setContentType("application/x-zip-compressed;charset=utf-8");
                processZip(response.getOutputStream(), file, outPutFileName, formate, true);
                break;
            case XLS:
                response.setContentType("application/octet-stream;charset=utf-8");
                processCommon(response.getOutputStream(), file, true);
                break;
            case APK:
                response.setContentType("application/octet-stream");
                processCommon(response.getOutputStream(), file, false);
                break;
            default:
                break;
        }
    }

    /**
     * 下载普通格式文件
     * @param out       输出流
     * @param file      文件
     * @param deleteFlag 下载完成后 是否删除服务上的文件  true：删除  false：不删除
     */
    public static void processCommon(OutputStream out, File file, boolean deleteFlag){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(FileUtils.readFileToByteArray(file));
            bis = new BufferedInputStream(byteArrayInputStream);
            bos = new BufferedOutputStream(out);
            int buffSize = 1024;
            byte[] buff = new byte[buffSize];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
            bos.flush();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (bis != null) {
                    bis.close();
                }
                if (bos != null) {
                    bos.close();
                }
                if (file != null && deleteFlag) {
                    file.delete();
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     *
     * 下载压缩文件
     * @param out               输出流
     * @param file              待压缩的文件
     * @param outPutFileName    压缩文件的名称
     * @param format            压缩文件的格式 format
     * @param deleteFlag        是否删除压缩文件
     */
    public static void processZip(OutputStream out, File file, String outPutFileName, DownloadFileFormatEnum format, boolean deleteFlag){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        File zipFile = null;
        try {
            //创建压缩文件的全路径
            zipFile = new File(file.getParent().concat(File.separator).concat(outPutFileName).concat(format.getSuffix()));
            //生成压缩文件
            ZipUtils.handleCompress(file.getAbsolutePath(), zipFile, true);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(FileUtils.readFileToByteArray(zipFile));
            bis = new BufferedInputStream(byteArrayInputStream);
            bos = new BufferedOutputStream(out);
            int buffSize = 1024;
            byte[] buff = new byte[buffSize];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (bis != null) {
                    bis.close();
                }
                if (bos != null) {
                    bos.close();
                }
                if (zipFile != null && deleteFlag) {
                    zipFile.delete();
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 计算下载文件的长度
     * @param remoteUrl
     * @return  1：计算
     */
    public static int computeDownloadRemoteUrlLength(String remoteUrl, List<Integer> returnLengthList){
        if(returnLengthList.isEmpty()){
            return 0;
        }
        URL url = null;
        URLConnection urlConnection = null;
        HttpURLConnection httpURLConnection = null;
        BufferedInputStream bis = null;
        try {
            url = new URL(remoteUrl);
            urlConnection = url.openConnection();
            //URLConnection设置连接超时，从主机读取数据超时 单位：毫秒
            urlConnection.setConnectTimeout(10000);
            urlConnection.setConnectTimeout(10000);
            // 设置通用的请求属性
            urlConnection.setRequestProperty("accept", "*/*");
            urlConnection.setRequestProperty("connection", "Keep-Alive");
            urlConnection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            urlConnection.connect();
            // 转化url连接为httpUrl连接
            httpURLConnection = (HttpURLConnection) urlConnection;
            int httpResult = httpURLConnection.getResponseCode();
            if (httpResult == HttpURLConnection.HTTP_OK) {
                int fileLength = urlConnection.getContentLength(); // 取数据长度
                log.info("计算下载文件长度={}，规则长度={}", fileLength, returnLengthList);
                //读取文件长度
                boolean result = returnLengthList.stream().anyMatch(item -> item == fileLength);
                if(result){
                    return 1;
                }
            }
        } catch (MalformedURLException e) {
            log.info("下载文件MalformedUR异常={}", ExceptionUtil.getExceptionInfo(e));
        } catch (IOException e) {
            log.info("computeDownloadRemoteUrlLength下载文件IO异常={}", ExceptionUtil.getExceptionInfo(e));
        } finally {
            if (httpURLConnection != null)
                httpURLConnection.disconnect();
        }
        return 0;
    }

    /**
     * 下载远程文件到本机
     * @param remoteUrl 远程文件
     * @param outFile   输出文件
     */
    public static void downloadRemoteUrlToLocal(String remoteUrl, File outFile){
        URL url = null;
        URLConnection urlConnection = null;
        HttpURLConnection httpURLConnection = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        try {
            url = new URL(remoteUrl);
            urlConnection = url.openConnection();
            //URLConnection设置连接超时，从主机读取数据超时 单位：毫秒
            urlConnection.setConnectTimeout(8000);
            // 设置通用的请求属性
            urlConnection.setRequestProperty("accept", "*/*");
            urlConnection.setRequestProperty("connection", "Keep-Alive");
            urlConnection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            urlConnection.connect();
            // 转化url连接为httpUrl连接
            httpURLConnection = (HttpURLConnection) urlConnection;
            int httpResult = httpURLConnection.getResponseCode();
            if (httpResult == HttpURLConnection.HTTP_OK) {
                int fileLength = urlConnection.getContentLength(); // 取数据长度
                //读取文件长度
                log.info("下载文件的长度=【{}】", fileLength);
                int size = 0;
                bis = new BufferedInputStream(httpURLConnection.getInputStream());
                int buffSize = 1024;
                byte[] buffer = new byte[buffSize];
                fos = openOutputStream(outFile);//自动创建文件、以及路径
                while ((size = bis.read(buffer)) != -1) {
                    fos.write(buffer, 0, size);
                }
                log.info("下载文件到=【{}】", outFile);
            }else{
                log.info("HTTP状={}", httpResult);
            }
        } catch (MalformedURLException e) {
            log.info("下载文件MalformedUR异常={}", ExceptionUtil.getExceptionInfo(e));
        } catch (IOException e) {
            log.info("downloadRemoteUrlToLocal下载文件IO异常={}", ExceptionUtil.getExceptionInfo(e));
        } finally {
            try {
                if(fos!=null){
                    fos.close();
                }
                if(bis!=null){
                    bis.close();
                }
                if (httpURLConnection != null)
                    httpURLConnection.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
                log.error("finally处理异常={}", ExceptionUtil.getExceptionInfo(e));
            }
        }
    }

    public static FileOutputStream openOutputStream(File file) throws IOException {
        if (file.exists()) {
            if (file.isDirectory()) {
                throw new IOException("File '" + file + "' exists but is a directory");
            }
            if (!file.canWrite()) {
                throw new IOException("File '" + file + "' cannot be written to");
            }
        } else {
            File parent = file.getParentFile();
            if (parent != null && !parent.mkdirs() && !parent.isDirectory()) {
                throw new IOException("Directory '" + parent + "' could not be created");
            }
        }

        return new FileOutputStream(file);
    }
}
